from .algo import Algorithm
from .objects import *
from multiprocessing import Queue, Pool, Process ,cpu_count ,Pipe
from time import sleep
import matplotlib.pyplot as plt
import time
import os

class Portfolio(object):
    algos = {}
    symbols = {}


    def __init__(self,name):
        self.name = name
        self.portfolio_eq = None
        self.portfolio_txns = {}

    def add_algo(self, name : str, algo: Algorithm):
        self.algos[name] = algo
        print(name)


    def run_algos_solo(self):
        results = []
        for algo_name in self.algos:
            algo = self.algos[algo_name]
            results.append(algo.run_port())

        results.append(algo.run())
        return results
        for result in results:
            print(result)

    def run_algos_parallelly(self):

        self.pool = Pool(cpu_count()-1)
        # self.pool = Pool(40)

        results = {}
        for algo_name in self.algos:
            algo= self.algos[algo_name]
            print(algo_name + " start")
            results[algo_name] = self.pool.apply_async(algo.run_port, args=())

        self.pool.close()
        self.pool.join()

        return results


    def update_portfolio(self,results):

        msg = "update_portfolio"
        print(msg)

        porfolio_detail = pd.DataFrame()

        init_eq = 0

        total_fees = 0

        for algo_name in self.algos:
            print(algo_name)
            algo_result = results[algo_name].get()

            name = algo_result["name"]
            allocation = algo_result["allocation"]
            daily_eq = algo_result["daily_eq"]
            porfolio_detail[name] = daily_eq['equity']
            init_eq = init_eq + allocation

            txns = algo_result["transactions"]

            self.portfolio_txns[name] = txns
            # algo_fees = txns.Txn_Fees.sum()
            # total_fees += algo_fees
            # algo_profit = algo_result['end_equity'] - algo_result["allocation"]

        # fill missing values
        missing_place = np.isnan(porfolio_detail)
        for algo_name in self.algos:
            algo_missing = missing_place[algo_name]
            for i in range(1, len(algo_missing)-1):
                if algo_missing[i]:
                    porfolio_detail[algo_name].iloc[i] = porfolio_detail[algo_name].iloc[i-1]
        self.init_eq = init_eq

        porfolio_detail.drop(porfolio_detail.index[0], inplace=True)

        self.porfolio_detail = porfolio_detail
        self.porfolio_detail.to_csv(os.path.abspath('.\\output\\') + "\\"+self.name+"_portfolio_detail.csv")
        self.portfolio_eq = porfolio_detail.sum(axis=1)
        self.daily_net_value = self.portfolio_eq /self.init_eq
        self.daily_net_value.to_csv(os.path.abspath('.\\output\\') + "\\"+self.name +"daily_net_value.csv")




    def cal_performance(self):
        # total return
        # annual return
        # num.trades
        # sharpe
        # dd
        # calmar
        # txn_cost
        inst_performance = pd.DataFrame(columns=["name", "symbol", "txns", "net_pl", "annual_return", "sharpe", "drawdown", "calmar"])
        # calculate inist performance

        pf_end_eq = 0
        total_fees = 0
        for algo_name in self.algos:
            # data for portfolio

            # data for instrument
            algo = self.algos[algo_name]
            transactions = self.portfolio_txns[algo_name]
            inst_eq = self.porfolio_detail[algo_name]
            inst_ret = (inst_eq -inst_eq.shift(1)) / algo.init_eq
            inst_ret = inst_ret.drop(inst_ret.index[0])
            inst_unit_value = pd.DataFrame(inst_eq/algo.init_eq)
            inst_unit_value["pre_max"] = inst_unit_value.cummax()
            inst_unit_value["drawdown"] = inst_unit_value[algo_name] - inst_unit_value["pre_max"]
            max_drawdown = min(inst_unit_value["drawdown"])
            max_dd_date =inst_unit_value["drawdown"].idxmin()


            pf_end_eq += inst_eq[-1]

            sum_fees = transactions[["txn_fees"]].sum()[0]
            total_fees += sum_fees
            net_profit = inst_eq[-1]- algo.init_eq
            annual_return = net_profit * 365 / (algo.init_eq* len(inst_eq))
            sharpe = np.mean(inst_ret)/ np.std(inst_ret) * np.sqrt(365)

            inst_performance.loc[algo_name, "Name"] = algo_name
            inst_performance.loc[algo_name, "n"] = algo.n
            inst_performance.loc[algo_name, "symbol"] = algo_name
            inst_performance.loc[algo_name, "txns"] = len(transactions)
            inst_performance.loc[algo_name, "net_pl"] = net_profit
            inst_performance.loc[algo_name, "sum_fees"] = sum_fees
            inst_performance.loc[algo_name, "fee_ratio"] = sum_fees/net_profit
            inst_performance.loc[algo_name, "annual_return"] = annual_return
            inst_performance.loc[algo_name, "sharpe"] = sharpe
            inst_performance.loc[algo_name, "drawdown"] = max_drawdown
            inst_performance.loc[algo_name, "max_dd_date"] = max_dd_date
            inst_performance.loc[algo_name, "calmar"] = annual_return/ max_drawdown * -1

        self.inst_performance  = inst_performance
        self.inst_performance.to_csv(os.path.abspath('.\\output\\') + "\\"+self.name +"_inst_perform.csv")

        # portfolio performance
        self.end_eq = pf_end_eq
        txn_nums = 0
        txn_volume = 0

        for algo_name in self.portfolio_txns:
            txn_nums += len(self.portfolio_txns[algo_name])
            txn_volume += abs(self.portfolio_txns[algo_name].txn_value).sum()


        net_profit = self.end_eq - self.init_eq
        annual_return = net_profit * 365 / (self.init_eq * len(self.portfolio_eq))
        return_rate = net_profit/ self.init_eq
        pf_ret = (self.portfolio_eq - self.portfolio_eq.shift(1)) / self.init_eq

        sharpe = np.mean(pf_ret) / np.std(pf_ret) * np.sqrt(365)

        pf_unit_value = pd.DataFrame({"unit_value" : self.portfolio_eq / self.init_eq})
        pf_unit_value["pre_max"] = pf_unit_value.cummax()
        pf_unit_value["drawdown"] = pf_unit_value["unit_value"] - pf_unit_value["pre_max"]
        max_drawdown = min(pf_unit_value["drawdown"])
        max_dd_date = pf_unit_value["drawdown"].idxmin()

        portfolio_performance = pd.DataFrame(columns=[ "txns", "net_pl", "annual_return", "sharpe", "drawdown", "calmar"])
        portfolio_performance.loc["portfolio", "annual_return"] = annual_return
        portfolio_performance.loc["portfolio", "return_rate"] = return_rate
        portfolio_performance.loc["portfolio","txns"] =txn_nums
        portfolio_performance.loc["portfolio","total_fees"] =total_fees
        portfolio_performance.loc["portfolio","Aannual_txns"] = int(txn_nums* 365/(len(self.algos) * len(pf_ret)))
        portfolio_performance.loc["portfolio", "net_pl"] = net_profit
        portfolio_performance.loc["portfolio", "profit_rate"] = net_profit/txn_volume
        portfolio_performance.loc["portfolio", "sharpe"] = sharpe
        portfolio_performance.loc["portfolio", "drawdown"] = max_drawdown
        portfolio_performance.loc["portfolio", "max_dd_date"]  = max_dd_date
        portfolio_performance.loc["portfolio", "calmar"] = annual_return/ max_drawdown * -1
        print(portfolio_performance.T)

        return





    def display(self):
        import matplotlib.pyplot as plt

        ax1 = plt.subplot(211)
        ax1.set_title("dailyNetValue")
        ax1.plot(self.daily_net_value.index, self.daily_net_value)

        # ax2 = plt.subplot(312, sharex=ax1)
        # ax2.set_title("parameters")
        # ax2.plot(self.inst_performance.n,self.inst_performance.sharpe)

        ax3 = plt.subplot(212, sharex=ax1)
        ax3.set_title("mkt_price")
        ax3.plot(np.log2(self.symbol.data.mkt_day[["close"]]))
        plt.show()

    def init(self):
        pass



if __name__ == '__main__':


    pass